/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.client;

import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.entity.DefaultExceptionProperties;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import io.iec.edp.caf.rpc.api.annotation.RpcParam;
import io.iec.edp.caf.rpc.api.annotation.RpcServiceMethod;
import io.iec.edp.caf.rpc.api.service.RpcClient;
import io.iec.edp.caf.rpc.api.support.Type;
import lombok.extern.slf4j.Slf4j;
import lombok.var;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.LinkedHashMap;

/**
 * @author Leon Huo
 */
@Slf4j
public class DynamicInvocationHandler implements InvocationHandler {

    private RpcClient rpcClient;

    private String serviceUnitName;

    private HashMap<String, String> rpcContext;

    public DynamicInvocationHandler(RpcClient rpcClient, String serviceUnitName, HashMap<String, String> rpcContext) {
        this.rpcClient = rpcClient;
        this.serviceUnitName = serviceUnitName;
        this.rpcContext = rpcContext;
    }

    /**
     * 可扩展处理点invoke
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //不支持调用toString equals hashCode等继承自Object的原生方法
        if (method.getDeclaringClass() == Object.class) {
            log.warn("not support this method : {}", method.getName());
            return null;
        }

        //如果方法上有rpcServiceMethod注解 则serviceId从注解中取 否则取方法名
        RpcServiceMethod rpcServiceMethod = method.getDeclaredAnnotation(RpcServiceMethod.class);
        String serviceId = rpcServiceMethod == null || "".equals(rpcServiceMethod.serviceId()) ? method.getDeclaringClass().getName() + "." + method.getName() : rpcServiceMethod.serviceId();

        //组装参数map
        LinkedHashMap<String, Object> paramMap = new LinkedHashMap<>();
        Parameter[] parameters = method.getParameters();
        var isStreamUpload = false;
        InputStream stream = null;
        var isStreamDownload = false;

        for (int i = 0; i < parameters.length; i++) {
            String name;
            RpcParam rpcParam = parameters[i].getDeclaredAnnotation(RpcParam.class);
            if (rpcParam == null) {
                throw new CAFRuntimeException(
                        DefaultExceptionProperties.SERVICE_UNIT,
                        DefaultExceptionProperties.RESOURCE_FILE,
                        "ECP_CAF_EXCP_0152",
                        new String[]{serviceId}, null, ExceptionLevel.Error, false
                );
            } else {
                if(rpcParam.streamParam()){
                    if(parameters[i].getType().getName().equalsIgnoreCase(InputStream.class.getName())){
                        isStreamUpload = true;
                        stream = (InputStream) args[i];
                    }else if(parameters[i].getType().getName().equalsIgnoreCase(HttpServletResponse.class.getName())){
                        isStreamDownload = true;
                    }
                }else{
                    name = rpcParam.paramName();
                    paramMap.put(name, args[i]);

                }
            }
        }

//        Class returnType = method.get();
        java.lang.reflect.Type returnValueType = method.getGenericReturnType();
        if(returnValueType.getTypeName().equalsIgnoreCase(void.class.getName())){
            returnValueType = String.class;
        }

        if(isStreamUpload){
            return rpcClient.invokeStream(stream,Type.fromReflect(returnValueType), serviceId,"1.0", serviceUnitName, paramMap, rpcContext);
        }else if(isStreamDownload){
            return rpcClient.invokeStream(serviceId,"1.0", serviceUnitName, paramMap, rpcContext);
        }else{
            return rpcClient.invoke(Type.fromReflect(returnValueType), serviceId, serviceUnitName, paramMap, rpcContext);
        }
    }
}
